using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;

namespace GameToolkit.UI
{
    public class TabToggleGroup : UIBehaviour, AsyncDataListView.IAsyncDataValidate
    {
        public event System.Action<IToggle> OnSelectedChanged;

        [SerializeField]
        private bool m_AllowSwitchOff = false;

        public UnityEvent m_OnSelectedToggleChagned = new UnityEvent();

        IToggle mSelected;
        public IToggle SelectedToggle
        {
            get { return mSelected; }
            private set
            {
                if (mSelected != value)
                {
                    mSelected = value;
#if UNITY_EDITOR
                    if (Application.isPlaying)
                    {
#endif
                        OnSelectedChanged?.Invoke(value);
                        m_OnSelectedToggleChagned.Invoke();
#if UNITY_EDITOR
                    }
#endif
                }
            }
        }

        /// <summary>
        /// Is it allowed that no toggle is switched on?
        /// </summary>
        /// <remarks>
        /// If this setting is enabled, pressing the toggle that is currently switched on will switch it off, so that no toggle is switched on. If this setting is disabled, pressing the toggle that is currently switched on will not change its state.
        /// Note that even if allowSwitchOff is false, the Toggle Group will not enforce its constraint right away if no toggles in the group are switched on when the scene is loaded or when the group is instantiated. It will only prevent the user from switching a toggle off.
        /// </remarks>
        public bool AllowSwitchOff
        {
            get
            {
                return m_AllowSwitchOff
#if UNITY_EDITOR
                    || !Application.isPlaying
#endif
                    ;
            }/* set { m_AllowSwitchOff = value; } */
        }

        private List<IToggle> m_Toggles = new List<IToggle>();
        bool mProtection;
        IToggle mPreferToggle;

        protected TabToggleGroup()
        { }

        protected override void OnEnable()
        {
            base.OnEnable();
            //InitToggleGroup();
            MainThread.RunOnMainThread(InitToggleGroup);
        }

        protected override void OnDisable()
        {
            mProtection = false;
            base.OnDisable();
        }

        //private void ValidateToggleIsInGroup(IToggle toggle)
        //{
        //    if (toggle == null || !m_Toggles.Contains(toggle))
        //        throw new ArgumentException(string.Format("Toggle {0} is not part of ToggleGroup {1}", new object[] { toggle, this }));
        //}

        ///// <summary>
        ///// Notify the group that the given toggle is enabled.
        ///// </summary>
        ///// <param name="toggle">The toggle that got triggered on</param>
        //public void NotifyToggleOn(IToggle toggle)
        //{
        //    ValidateToggleIsInGroup(toggle);

        //    // disable all toggles in the group
        //    for (var i = 0; i < m_Toggles.Count; i++)
        //    {
        //        if (m_Toggles[i] == toggle)
        //            continue;

        //        m_Toggles[i].IsOn = false;
        //    }
        //    SelectedToggle = toggle;
        //}

        public void ValidateAsyncData()
        {
#if UNITY_EDITOR
            if (Application.isPlaying)
#endif
                m_OnSelectedToggleChagned.Invoke();
        }

        /// <summary>
        /// Register a toggle with the toggle group so it is watched for changes and notified if another toggle in the group changes.
        /// </summary>
        /// <param name="toggle">The toggle to register with the group.</param>
        public void RegisterToggle(IToggle toggle)
        {
            if (!m_Toggles.Contains(toggle))
            {
                m_Toggles.Add(toggle);
                if (toggle.IsOn)
                    mPreferToggle = toggle;
                toggle.IsOnChanged += ValidateToggle;
                mProtection |= MainThread.RunOnMainThread(InitToggleGroup, EExecuteEvent.Update, !mProtection);
                //if (SelectedToggle != null && SelectedToggle.IsOn)
                //{
                //    toggle.IsOn = false;
                //}
                //else if(toggle.IsOn)
                //{
                //    mProtection = true;
                //    var old = SelectedToggle;
                //    if (old != toggle && old != null)
                //        old.IsOn = false;
                //    mProtection = false;
                //    SelectedToggle = toggle;
                //}
                //else if(!m_AllowSwitchOff && SelectedToggle == null)
                //{
                //    MainThread.RunOnMainThread(SetupDefaultSelected);
                //}
            }
        }

        /// <summary>
        /// Unregister a toggle from the group.
        /// </summary>
        /// <param name="toggle">The toggle to remove.</param>
        public void UnregisterToggle(IToggle toggle)
        {
            if (m_Toggles.Remove(toggle))
            {
                toggle.IsOnChanged -= ValidateToggle;
                mProtection |= MainThread.RunOnMainThread(InitToggleGroup, EExecuteEvent.Update, !mProtection);
            }
        }

        void InitToggleGroup()
        {
            if (this == null || !IsActive())
                return;
            mProtection = true;
            IToggle isOn = mPreferToggle != null && mPreferToggle.IsOn && m_Toggles.Contains(mPreferToggle) ? mPreferToggle : null;
            for (int i = 0; i < m_Toggles.Count; i++)
            {
                var tog = m_Toggles[i];
                if (isOn == null && tog.IsOn)
                {
                    isOn = tog;
                }
                else if (isOn != tog)
                {
                    tog.IsOn = false;
                }
            }
            if (isOn == null && !AllowSwitchOff && m_Toggles.Count > 0)
            {
                isOn = m_Toggles[0];
                isOn.IsOn = true;
            }
            SelectedToggle = isOn;
            mPreferToggle = isOn;
            mProtection = false;
        }

        void ValidateToggle(IToggle tog)
        {
            if (tog.IsOn)
            {
                mPreferToggle = tog;
                if (!mProtection && tog != mSelected)
                {
                    mProtection = true;
                    var old = mSelected;
                    if (old != null)
                        old.IsOn = false;
                    SelectedToggle = tog;
                    mProtection = false;
                }
            }
            else if (!mProtection && !tog.IsOn && mSelected == tog)
            {
                if (!AllowSwitchOff)
                    tog.IsOn = true;
                else
                    SelectedToggle = null;
            }
        }

        ///// <summary>
        ///// Are any of the toggles on?
        ///// </summary>
        ///// <returns>Are and of the toggles on?</returns>
        //public bool AnyTogglesOn()
        //{
        //    return mSelected != null && mSelected.IsOn;
        //    //return m_Toggles.Find(x => x.IsOn) != null;
        //}

        /// <summary>
        /// Switch all toggles off.
        /// </summary>
        /// <remarks>
        /// This method can be used to switch all toggles off, regardless of whether the allowSwitchOff property is enabled or not.
        /// </remarks>
        public void SetAllTogglesOff()
        {
            //bool oldAllowSwitchOff = m_AllowSwitchOff;
            //m_AllowSwitchOff = true;

            if (AllowSwitchOff)
            {
                for (var i = 0; i < m_Toggles.Count; i++)
                {
                    m_Toggles[i].IsOn = false;
                }
            }
            //m_AllowSwitchOff = oldAllowSwitchOff;
            //SelectedToggle = null;
        }
    }
}